/*
 * Copyright 2024 The CHANGLEI Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

part of '../px.dart';

/// 像素大小
extension RadiusToPx on Radius {
  /// 像素大小
  PxRadius get px => PxRadius.elliptical(x.px, y.px);

  /// 像素大小
  PxRadius toPx(BuildContext context) {
    return PxRadius.elliptical(
      x.toPx(context),
      y.toPx(context),
    );
  }
}

/// Created by changlei on 2024/4/23.
///
/// See [Radius]
class PxRadius implements _Px<Radius> {
  /// See [Radius.circular]
  const PxRadius.circular(PxDouble radius) : this.elliptical(radius, radius);

  /// See [Radius.elliptical]
  const PxRadius.elliptical(this.x, this.y);

  /// See [Radius.x]
  final PxDouble x;

  /// See [Radius.y]
  final PxDouble y;

  /// See [Radius.zero]
  static const PxRadius zero = PxRadius.circular(PxDouble.zero);

  /// See [Radius.clamp]
  PxRadius clamp({PxRadius? minimum, PxRadius? maximum}) {
    minimum ??= const PxRadius.circular(PxDouble.negativeInfinity);
    maximum ??= const PxRadius.circular(PxDouble.infinity);
    return PxRadius.elliptical(
      x.clamp(minimum.x, maximum.x).toDouble(),
      y.clamp(minimum.y, maximum.y).toDouble(),
    );
  }

  /// See [Radius.clampValues]
  PxRadius clampValues({
    PxDouble? minimumX,
    PxDouble? minimumY,
    PxDouble? maximumX,
    PxDouble? maximumY,
  }) {
    return PxRadius.elliptical(
      x.clamp(minimumX ?? PxDouble.negativeInfinity, maximumX ?? PxDouble.infinity).toDouble(),
      y.clamp(minimumY ?? PxDouble.negativeInfinity, maximumY ?? PxDouble.infinity).toDouble(),
    );
  }

  /// See [Radius.-]
  PxRadius operator -() => PxRadius.elliptical(-x, -y);

  /// See [Radius.-]
  PxRadius operator -(PxRadius other) => PxRadius.elliptical(x - other.x, y - other.y);

  /// See [Radius.+]
  PxRadius operator +(PxRadius other) => PxRadius.elliptical(x + other.x, y + other.y);

  /// See [Radius.*]
  PxRadius operator *(PxDouble operand) => PxRadius.elliptical(x * operand, y * operand);

  /// See [Radius./]
  PxRadius operator /(PxDouble operand) => PxRadius.elliptical(x / operand, y / operand);

  /// See [Radius.~/]
  PxRadius operator ~/(PxDouble operand) => PxRadius.elliptical((x ~/ operand).toDouble(), (y ~/ operand).toDouble());

  /// See [Radius.%]
  PxRadius operator %(PxDouble operand) => PxRadius.elliptical(x % operand, y % operand);

  /// See [Radius.lerp]
  static PxRadius? lerp(PxRadius? a, PxRadius? b, double t) {
    if (b == null) {
      if (a == null) {
        return null;
      } else {
        final k = (1.0 - t).px;
        return PxRadius.elliptical(a.x * k, a.y * k);
      }
    } else {
      if (a == null) {
        return PxRadius.elliptical(b.x * t.px, b.y * t.px);
      } else {
        return PxRadius.elliptical(
          _lerpPxDouble(a.x, b.x, t),
          _lerpPxDouble(a.y, b.y, t),
        );
      }
    }
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (runtimeType != other.runtimeType) {
      return false;
    }

    return other is PxRadius && other.x == x && other.y == y;
  }

  @override
  int get hashCode => Object.hash(x, y);

  @override
  String toString() {
    return x == y
        ? 'PxRadius.circular(${x.toStringAsFixed(1)})'
        : 'PxRadius.elliptical(${x.toStringAsFixed(1)}, '
            '${y.toStringAsFixed(1)})';
  }

  @override
  Radius toDp(BuildContext context) {
    return Radius.elliptical(
      x.toDp(context),
      y.toDp(context),
    );
  }
}
